home *** CD-ROM | disk | FTP | other *** search
- """Interfaces for launching and remotely controlling Web browsers."""
-
- import os
- import sys
-
- __all__ = ["Error", "open", "get", "register"]
-
- class Error(Exception):
- pass
-
- _browsers = {} # Dictionary of available browser controllers
- _tryorder = [] # Preference order of available browsers
-
- def register(name, klass, instance=None):
- """Register a browser connector and, optionally, connection."""
- _browsers[name.lower()] = [klass, instance]
-
- def get(using=None):
- """Return a browser launcher instance appropriate for the environment."""
- if using:
- alternatives = [using]
- else:
- alternatives = _tryorder
- for browser in alternatives:
- if browser.find('%s') > -1:
- # User gave us a command line, don't mess with it.
- return GenericBrowser(browser)
- else:
- # User gave us a browser name.
- try:
- command = _browsers[browser.lower()]
- except KeyError:
- command = _synthesize(browser)
- if command[1] is None:
- return command[0]()
- else:
- return command[1]
- raise Error("could not locate runnable browser")
-
- # Please note: the following definition hides a builtin function.
-
- def open(url, new=0, autoraise=1):
- get().open(url, new, autoraise)
-
- def open_new(url):
- get().open(url, 1)
-
-
- def _synthesize(browser):
- """Attempt to synthesize a controller base on existing controllers.
-
- This is useful to create a controller when a user specifies a path to
- an entry in the BROWSER environment variable -- we can copy a general
- controller to operate using a specific installation of the desired
- browser in this way.
-
- If we can't create a controller in this way, or if there is no
- executable for the requested browser, return [None, None].
-
- """
- if not os.path.exists(browser):
- return [None, None]
- name = os.path.basename(browser)
- try:
- command = _browsers[name.lower()]
- except KeyError:
- return [None, None]
- # now attempt to clone to fit the new name:
- controller = command[1]
- if controller and name.lower() == controller.basename:
- import copy
- controller = copy.copy(controller)
- controller.name = browser
- controller.basename = os.path.basename(browser)
- register(browser, None, controller)
- return [None, controller]
- return [None, None]
-
-
- def _iscommand(cmd):
- """Return true if cmd can be found on the executable search path."""
- path = os.environ.get("PATH")
- if not path:
- return 0
- for d in path.split(os.pathsep):
- exe = os.path.join(d, cmd)
- if os.path.isfile(exe):
- return 1
- return 0
-
-
- PROCESS_CREATION_DELAY = 4
-
-
- class GenericBrowser:
- def __init__(self, cmd):
- self.name, self.args = cmd.split(None, 1)
- self.basename = os.path.basename(self.name)
-
- def open(self, url, new=0, autoraise=1):
- assert "'" not in url
- command = "%s %s" % (self.name, self.args)
- os.system(command % url)
-
- def open_new(self, url):
- self.open(url)
-
-
- class Netscape:
- "Launcher class for Netscape browsers."
- def __init__(self, name):
- self.name = name
- self.basename = os.path.basename(name)
-
- def _remote(self, action, autoraise):
- raise_opt = ("-noraise", "-raise")[autoraise]
- cmd = "%s %s -remote '%s' >/dev/null 2>&1" % (self.name,
- raise_opt,
- action)
- rc = os.system(cmd)
- if rc:
- import time
- os.system("%s &" % self.name)
- time.sleep(PROCESS_CREATION_DELAY)
- rc = os.system(cmd)
- return not rc
-
- def open(self, url, new=0, autoraise=1):
- if new:
- self._remote("openURL(%s, new-window)"%url, autoraise)
- else:
- self._remote("openURL(%s)" % url, autoraise)
-
- def open_new(self, url):
- self.open(url, 1)
-
-
- class Konqueror:
- """Controller for the KDE File Manager (kfm, or Konqueror).
-
- See http://developer.kde.org/documentation/other/kfmclient.html
- for more information on the Konqueror remote-control interface.
-
- """
- def __init__(self):
- if _iscommand("konqueror"):
- self.name = self.basename = "konqueror"
- else:
- self.name = self.basename = "kfm"
-
- def _remote(self, action):
- cmd = "kfmclient %s >/dev/null 2>&1" % action
- rc = os.system(cmd)
- if rc:
- import time
- if self.basename == "konqueror":
- os.system(self.name + " --silent &")
- else:
- os.system(self.name + " -d &")
- time.sleep(PROCESS_CREATION_DELAY)
- rc = os.system(cmd)
- return not rc
-
- def open(self, url, new=1, autoraise=1):
- # XXX Currently I know no way to prevent KFM from
- # opening a new win.
- assert "'" not in url
- self._remote("openURL '%s'" % url)
-
- open_new = open
-
-
- class Grail:
- # There should be a way to maintain a connection to Grail, but the
- # Grail remote control protocol doesn't really allow that at this
- # point. It probably neverwill!
- def _find_grail_rc(self):
- import glob
- import pwd
- import socket
- import tempfile
- tempdir = os.path.join(tempfile.gettempdir(),
- ".grail-unix")
- user = pwd.getpwuid(os.getuid())[0]
- filename = os.path.join(tempdir, user + "-*")
- maybes = glob.glob(filename)
- if not maybes:
- return None
- s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
- for fn in maybes:
- # need to PING each one until we find one that's live
- try:
- s.connect(fn)
- except socket.error:
- # no good; attempt to clean it out, but don't fail:
- try:
- os.unlink(fn)
- except IOError:
- pass
- else:
- return s
-
- def _remote(self, action):
- s = self._find_grail_rc()
- if not s:
- return 0
- s.send(action)
- s.close()
- return 1
-
- def open(self, url, new=0, autoraise=1):
- if new:
- self._remote("LOADNEW " + url)
- else:
- self._remote("LOAD " + url)
-
- def open_new(self, url):
- self.open(url, 1)
-
-
- class WindowsDefault:
- def open(self, url, new=0, autoraise=1):
- os.startfile(url)
-
- def open_new(self, url):
- self.open(url)
-
- #
- # Platform support for Unix
- #
-
- # This is the right test because all these Unix browsers require either
- # a console terminal of an X display to run. Note that we cannot split
- # the TERM and DISPLAY cases, because we might be running Python from inside
- # an xterm.
- if os.environ.get("TERM") or os.environ.get("DISPLAY"):
- _tryorder = ["mozilla","netscape","kfm","grail","links","lynx","w3m"]
-
- # Easy cases first -- register console browsers if we have them.
- if os.environ.get("TERM"):
- # The Links browser <http://artax.karlin.mff.cuni.cz/~mikulas/links/>
- if _iscommand("links"):
- register("links", None, GenericBrowser("links '%s'"))
- # The Lynx browser <http://lynx.browser.org/>
- if _iscommand("lynx"):
- register("lynx", None, GenericBrowser("lynx '%s'"))
- # The w3m browser <http://ei5nazha.yz.yamagata-u.ac.jp/~aito/w3m/eng/>
- if _iscommand("w3m"):
- register("w3m", None, GenericBrowser("w3m '%s'"))
-
- # X browsers have more in the way of options
- if os.environ.get("DISPLAY"):
- # First, the Netscape series
- if _iscommand("mozilla"):
- register("mozilla", None, Netscape("mozilla"))
- if _iscommand("netscape"):
- register("netscape", None, Netscape("netscape"))
-
- # Next, Mosaic -- old but still in use.
- if _iscommand("mosaic"):
- register("mosaic", None, GenericBrowser(
- "mosaic '%s' >/dev/null &"))
-
- # Konqueror/kfm, the KDE browser.
- if _iscommand("kfm") or _iscommand("konqueror"):
- register("kfm", Konqueror, Konqueror())
-
- # Grail, the Python browser.
- if _iscommand("grail"):
- register("grail", Grail, None)
-
-
- class InternetConfig:
- def open(self, url, new=0, autoraise=1):
- ic.launchurl(url)
-
- def open_new(self, url):
- self.open(url)
-
-
- #
- # Platform support for Windows
- #
-
- if sys.platform[:3] == "win":
- _tryorder = ["netscape", "windows-default"]
- register("windows-default", WindowsDefault)
-
- #
- # Platform support for MacOS
- #
-
- try:
- import ic
- except ImportError:
- pass
- else:
- # internet-config is the only supported controller on MacOS,
- # so don't mess with the default!
- _tryorder = ["internet-config"]
- register("internet-config", InternetConfig)
-
- #
- # Platform support for OS/2
- #
-
- if sys.platform[:3] == "os2" and _iscommand("netscape.exe"):
- _tryorder = ["os2netscape"]
- register("os2netscape", None,
- GenericBrowser("start netscape.exe %s"))
-
- # OK, now that we know what the default preference orders for each
- # platform are, allow user to override them with the BROWSER variable.
- #
- if os.environ.has_key("BROWSER"):
- # It's the user's responsibility to register handlers for any unknown
- # browser referenced by this value, before calling open().
- _tryorder = os.environ["BROWSER"].split(os.pathsep)
-
- for cmd in _tryorder:
- if not _browsers.has_key(cmd.lower()):
- if _iscommand(cmd.lower()):
- register(cmd.lower(), None, GenericBrowser(
- "%s '%%s'" % cmd.lower()))
- cmd = None # to make del work if _tryorder was empty
- del cmd
-
- _tryorder = filter(lambda x: _browsers.has_key(x.lower())
- or x.find("%s") > -1, _tryorder)
- # what to do if _tryorder is now empty?
-